package l2ft.gameserver.model.entity.residence;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import l2ft.commons.dao.JdbcEntityState;
import l2ft.commons.math.SafeMath;
import l2ft.gameserver.Config;
import l2ft.gameserver.dao.CastleDAO;
import l2ft.gameserver.dao.CastleHiredGuardDAO;
import l2ft.gameserver.dao.ClanDataDAO;
import l2ft.gameserver.data.xml.holder.ResidenceHolder;
import l2ft.gameserver.database.DatabaseFactory;
import l2ft.gameserver.model.Player;
import l2ft.gameserver.model.items.ItemInstance;
import l2ft.gameserver.model.items.Warehouse;
import l2ft.gameserver.model.pledge.Clan;
import l2ft.gameserver.network.l2.components.CustomMessage;
import l2ft.gameserver.network.l2.components.NpcString;
import l2ft.gameserver.templates.StatsSet;
import l2ft.gameserver.templates.item.ItemTemplate;
import l2ft.gameserver.templates.item.support.MerchantGuard;
import l2ft.gameserver.utils.GameStats;
import l2ft.gameserver.utils.Log;

import org.napile.primitive.maps.IntObjectMap;
import org.napile.primitive.maps.impl.CTreeIntObjectMap;
import org.napile.primitive.maps.impl.HashIntObjectMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@SuppressWarnings("rawtypes")
public class Castle extends Residence
{
	private static final Logger _log = LoggerFactory.getLogger(Castle.class);

	private final IntObjectMap<MerchantGuard> _merchantGuards = new HashIntObjectMap<MerchantGuard>();
	private final IntObjectMap<List> _relatedFortresses = new CTreeIntObjectMap<List> ();
	private Dominion _dominion;

	private int _TaxPercent;
	private double _TaxRate;
	private long _treasury;
	private long _collectedShops;

	private final NpcString _npcStringName;

	private Set<ItemInstance> _spawnMerchantTickets = new CopyOnWriteArraySet<ItemInstance>();

	public Castle(StatsSet set)
	{
		super(set);
		_npcStringName = NpcString.valueOf(1001000 + _id);
	}

	@SuppressWarnings("unchecked")
	@Override
	public void init()
	{
		super.init();

		for(IntObjectMap.Entry<List> entry : _relatedFortresses.entrySet())
		{
			_relatedFortresses.remove(entry.getKey());

			List<Integer> list = entry.getValue();
			List<Fortress> list2 = new ArrayList<Fortress>(list.size());
			for(int i : list)
			{
				Fortress fortress = ResidenceHolder.getInstance().getResidence(Fortress.class, i);
				if(fortress == null)
					continue;

				list2.add(fortress);

				fortress.addRelatedCastle(this);
			}
			_relatedFortresses.put(entry.getKey(), list2);
		}
	}

	@Override
	public ResidenceType getType()
	{
		return ResidenceType.Castle;
	}

	// This method sets the castle owner; null here means give it back to NPC
	@Override
	public void changeOwner(Clan newOwner)
	{
		// ???? ???? ??? ?????? ?????-???? ??????/?????????, ???????? ???.
		if(newOwner != null)
		{
			if(newOwner.getHasFortress() != 0)
			{
				Fortress oldFortress = ResidenceHolder.getInstance().getResidence(Fortress.class, newOwner.getHasFortress());
				if(oldFortress != null)
					oldFortress.changeOwner(null);
			}
			if(newOwner.getCastle() != 0)
			{
				Castle oldCastle = ResidenceHolder.getInstance().getResidence(Castle.class, newOwner.getCastle());
				if(oldCastle != null)
					oldCastle.changeOwner(null);
			}
		}

		Clan oldOwner = null;
		// ???? ???? ?????? ??? ???-?? ??????, ???????? ? ???? ?????
		if(getOwnerId() > 0 && (newOwner == null || newOwner.getClanId() != getOwnerId()))
		{
			// ??????? ???????? ????? ? ??????? ?????????
			removeSkills();
			getDominion().changeOwner(null);
			getDominion().removeSkills();

			// ??????? ?????
			setTaxPercent(null, 0);
			cancelCycleTask();

			oldOwner = getOwner();
			if(oldOwner != null)
			{
				long amount = getTreasury();
				if(amount > 0)
				{
					Warehouse warehouse = oldOwner.getWarehouse();
					if(warehouse != null)
					{
						warehouse.addItem(ItemTemplate.ITEM_ID_ADENA, amount);
						addToTreasuryNoTax(-amount, false);
						Log.LogGame("Castle Treasury", getName() + "|" + -amount + "|Castle:changeOwner");
					}
				}

				for(Player clanMember : oldOwner.getOnlineMembers(0))
					if(clanMember != null && clanMember.getInventory() != null)
						clanMember.getInventory().validateItems();

				oldOwner.setHasCastle(0);
			}
		}

		if(newOwner != null)
			newOwner.setHasCastle(getId());

		updateOwnerInDB(newOwner);

		rewardSkills();

		update();
	}

	// This method loads castle
	@Override
	protected void loadData()
	{
		_TaxPercent = 0;
		_TaxRate = 0;
		_treasury = 0;

		_owner = ClanDataDAO.getInstance().getOwner(this);
		CastleDAO.getInstance().select(this);
		CastleHiredGuardDAO.getInstance().load(this);
	}

	public void setTaxPercent(int p)
	{
		_TaxPercent = Math.min(Math.max(0, p), 100);
		_TaxRate = _TaxPercent / 100.0;
	}

	public void setTreasury(long t)
	{
		_treasury = t;
	}

	private void updateOwnerInDB(Clan clan)
	{
		_owner = clan; // Update owner id property

		try (Connection con = DatabaseFactory.getConnection())
		{
			try(PreparedStatement statement = con.prepareStatement("UPDATE clan_data SET hasCastle=0 WHERE hasCastle=? LIMIT 1"))
			{
				statement.setInt(1, getId());
				statement.execute();
			}

			if(clan != null)
			{
				try(PreparedStatement statement = con.prepareStatement("UPDATE clan_data SET hasCastle=? WHERE clan_id=? LIMIT 1"))
				{
					statement.setInt(1, getId());
					statement.setInt(2, getOwnerId());
					statement.execute();
				}
				
				clan.broadcastClanStatus(true, false, false);
			}
		}
		catch(Exception e)
		{
			_log.error("", e);
		}
	}

	public int getTaxPercent()
	{
		if(_TaxPercent > 5)
			_TaxPercent = 5;
		return _TaxPercent;
	}

	public int getTaxPercent0()
	{
		return _TaxPercent;
	}

	public long getCollectedShops()
	{
		return _collectedShops;
	}

	public void setCollectedShops(long value)
	{
		_collectedShops = value;
	}

	// This method add to the treasury
	/** Add amount to castle instance's treasury (warehouse). */
	public void addToTreasury(long amount, boolean shop)
	{
		if(getOwnerId() <= 0)
			return;

		if(amount == 0)
			return;

		if(amount > 1 && _id != 5 && _id != 8) // If current castle instance is not Aden or Rune
		{
			Castle royal = ResidenceHolder.getInstance().getResidence(Castle.class, _id >= 7 ? 8 : 5);
			if(royal != null)
			{
				long royalTax = (long) (amount * royal.getTaxRate()); // Find out what royal castle gets from the current castle instance's income
				if(royal.getOwnerId() > 0)
				{
					royal.addToTreasury(royalTax, shop); // Only bother to really add the tax to the treasury if not npc owned
					if(_id == 5)
						Log.LogGame("Castle Treasury", "Aden|" + royalTax + "|Castle:adenTax");
					else if(_id == 8)
						Log.LogGame("Castle Treasury", "Rune|" + royalTax + "|Castle:runeTax");
				}

				amount -= royalTax; // Subtract royal castle income from current castle instance's income
			}
		}

		addToTreasuryNoTax(amount, shop);
	}

	/** Add amount to castle instance's treasury (warehouse), no tax paying. */
	public void addToTreasuryNoTax(long amount, boolean shop)
	{
		if(getOwnerId() <= 0)
			return;

		if(amount == 0)
			return;

		if(Config.RATE_DROP_ADENA < 20)
			GameStats.addAdena(amount);

		// Add to the current treasury total.  Use "-" to substract from treasury
		_treasury = SafeMath.addAndLimit(_treasury, amount);

		if(shop)
			_collectedShops += amount;

		setJdbcState(JdbcEntityState.UPDATED);
		update();
	}

	// This method updates the castle tax rate
	public void setTaxPercent(Player activeChar, int taxPercent)
	{
		setTaxPercent(taxPercent);

		setJdbcState(JdbcEntityState.UPDATED);
		update();

		if(activeChar != null)
			activeChar.sendMessage(new CustomMessage("l2ft.gameserver.model.entity.Castle.OutOfControl.CastleTaxChangetTo", activeChar).addString(getName()).addNumber(taxPercent));
	}

	public double getTaxRate()
	{
		if(_TaxRate > 0.05)
			_TaxRate = 0.05;
		return _TaxRate;
	}

	public long getTreasury()
	{
		return _treasury;
	}

	public Dominion getDominion()
	{
		return _dominion;
	}

	public void setDominion(Dominion dominion)
	{
		_dominion = dominion;
	}

	@SuppressWarnings("unchecked")
	public void addRelatedFortress(int type, int fortress)
	{
		List<Integer> fortresses = _relatedFortresses.get(type);
		if(fortresses == null)
			_relatedFortresses.put(type, fortresses = new ArrayList<Integer>());

		fortresses.add(fortress);
	}

	public int getDomainFortressContract()
	{
		List<Fortress> list = _relatedFortresses.get(Fortress.DOMAIN);
		if(list == null)
			return 0;
		for(Fortress f : list)
			if(f.getContractState() == Fortress.CONTRACT_WITH_CASTLE && f.getCastleId() == getId())
				return f.getId();
		return 0;
	}

	@Override
	public void update()
	{
		CastleDAO.getInstance().update(this);
	}

	public NpcString getNpcStringName()
	{
		return _npcStringName;
	}

	public IntObjectMap<List> getRelatedFortresses()
	{
		return _relatedFortresses;
	}

	public void addMerchantGuard(MerchantGuard merchantGuard)
	{
		_merchantGuards.put(merchantGuard.getItemId(), merchantGuard);
	}

	public MerchantGuard getMerchantGuard(int itemId)
	{
		return _merchantGuards.get(itemId);
	}

	public IntObjectMap<MerchantGuard> getMerchantGuards()
	{
		return _merchantGuards;
	}

	public Set<ItemInstance> getSpawnMerchantTickets()
	{
		return _spawnMerchantTickets;
	}

	@Override
	public void startCycleTask()
	{}
}